# $Id: ntp.py 9227e81e919a 2012/08/27 15:49:57 pgurumur $
# Copyright (c) 2009 Prabhu Gurumurthy  <pgurumur@gmail.com>
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

__version__  = "$Revision: 9227e81e919a $"
__author__   = "$Author: pgurumur $"
__modified__ = "$Date: 2012-08-27 15:49:57Z $"

# essential imports
import socket
import struct
import time

# Local imports
import udp

class NtpError(Exception):
   pass

class sntp(udp.udp):
   def __init__(self, Host, Port = 123):
      super(sntp, self).__init__(Host, Port)

      self._ntpRecvTime = None
      self._initData = '\x23' + 47 * '\0'
      self._time = 0
      self._epoch = 2208988800L
      self._initTime = 0
      self._ipRef = None
      self._server = None
      self._port = None

   def Init(self):
      self._initTime = time.time()
      return self._initData

   def GetReferenceId(self):
      return self._ipRef

   def GetTime(self):
      return self._ntpRecvTime

   def ProcessNtpData(self, data):
      if data:
         ntpData = struct.unpack("!12I", data)
         leapInfo = (int(ntpData[0]) & int(0xc0000000)) >> 30
         version = (int(ntpData[0]) & int(0x38000000)) >> 27
         mode = (int(ntpData[0]) & int(0x07000000)) >> 24
         stratum = (int(ntpData[0]) & int(0x00ff0000)) >> 16
         poll =  2 ** ((int(ntpData[0]) & int(0x0000ff00)) >> 8)
         precision = (int(ntpData[0]) & int(0x000000ff))
         if precision > 127:
            precision = precision - 255

         dispersion = (int(ntpData[2]) & int(0x00030000)) >> 16
         referId = int(ntpData[3])
         self._ipRef = socket.inet_ntoa(struct.pack(">L", referId))

         afterRecv = time.time()
         time_2 = self._ntpTime(ntpData[8:10])
         time_3 = self._ntpTime(ntpData[10:12])

         delay = (afterRecv - self._initTime) - (time_2 - time_3)
         offset = ((time_2 - self._initTime) + (time_3 - afterRecv)) / 2.

         recvTime = struct.unpack("!12I", data)[10]
         recvTime -= self._epoch
         self._ntpRecvTime = recvTime

         retString = time.ctime(recvTime)
         return (retString, offset)

   def _ntpTime(self, tm):
      return tm[0] - int(self._epoch) + \
             float((tm[1] >> 4) & 0xfffffff) / 0x10000000
